﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
using GISAzureCore;
using GISCore.Geometry;
using GISCore.Geometry.Intersectors;
using GISCore.Geometry.Partition;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Diagnostics;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;

namespace GISWorkerRole
{
    public class WorkerRole : RoleEntryPoint 
    {
        internal int MsgAmt = 2;
        CloudStorageAccount _account;
        
        public override void Run()
        {
            Trace.WriteLine("GISWorkerRole entry point called", "Information");
            CloudQueueClient queueClient = _account.CreateCloudQueueClient();
            CloudQueue sweepItemsQueue = queueClient.GetQueueReference(DataConstants.SweepItemQueue);
            CloudQueue mergeItemsQueue = queueClient.GetQueueReference(DataConstants.MergeItemQueue);
            CloudBlobClient blobClient = _account.CreateCloudBlobClient();
            CloudBlobContainer sweepItemsContainer = blobClient.GetContainerReference(DataConstants.SweepItemContainerBlob);

            while (true)
            {
                if (sweepItemsQueue.Exists())
                {
                    IEnumerable<CloudQueueMessage> msgs = null;
                    try
                    {
                        msgs = sweepItemsQueue.GetMessages(MsgAmt, TimeSpan.FromMinutes(30.0));
                        foreach (var msg in msgs)
                        {
                            _handleMessage(msg, sweepItemsContainer);
                            sweepItemsQueue.DeleteMessage(msg);
                            mergeItemsQueue.AddMessage(new CloudQueueMessage(msg.AsString), TimeSpan.FromMinutes(30.0));
                        }
                    }
                    catch (StorageClientException sce)
                    {
                        Trace.WriteLine(sce.Message, "Error");
                    }
                    catch (Exception e)
                    {
                        Trace.WriteLine(e.Message, "Error");
                    }
                }

                Thread.Sleep(1000);
            }
            
        }

        private void _handleMessage(CloudQueueMessage msg, CloudBlobContainer sweepItemsContainer)
        {
            Debug.Assert(msg != null);
            Debug.Assert(sweepItemsContainer != null);         
            
            Trace.WriteLine("GISWorkerRole Handling message", "Information");
            Cell cell = null;
            
            // get the cell data from the corresponding blob
            var blob = sweepItemsContainer.GetBlobReference(msg.AsString);                    
            using (BlobStream stream = blob.OpenRead())
            {
                BinaryFormatter bf = new BinaryFormatter();
                cell = bf.Deserialize(stream) as Cell;
            }

            // find intersections and add them to the cell
            // as well as reconstruct the cell
            IEnumerable<PostPolygon> polys = _sweepAndReconstruct(cell, blob);         
           
            // if we successfully processed this msg 
            // reserialize the data back to the blob for final processing
            using (BlobStream stream = blob.OpenWrite())
            {
                BinaryFormatter bf = new BinaryFormatter();
                bf.Serialize(stream, cell);
            }
         
            Trace.WriteLine("GISWorkerRole Handled message", "Information");
        }

        private IEnumerable<PostPolygon> _sweepAndReconstruct(ICellable icell, CloudBlob blob)
        {
            Cell cell = icell as Cell;
            IIntersector sweeper = new SweepLine();
            IReconstuctionable reconstructor = new PolygonReconstuctor();
            Trace.WriteLine(string.Format("Processing Cell {0}", cell), "Information");
            var lines = cell.getLines();
            var ipoints = sweeper.Intersect(lines);
            cell.setIntersectPoint(ipoints);
            //Xi has changed the interface.
            return reconstructor.reconstruct(lines, cell, cell.xmin, cell.xmax, cell.ymin, cell.ymax);
        }
         
        public override bool OnStart()
        {
            // Set the maximum number of concurrent connections 
            ServicePointManager.DefaultConnectionLimit = 12;

            // For information on handling configuration changes
            // see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
            CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
            {
                // Provide the configSetter with the initial value
                configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));

                RoleEnvironment.Changed += (sender, arg) =>
                {
                    foreach (var change in arg.Changes)
                    {
                        var configChange = change as RoleEnvironmentConfigurationSettingChange;
                        if (configChange != null)
                        {
                            RoleEnvironment.GetConfigurationSettingValue(
                                configChange.ConfigurationSettingName);
                        }
                    }
                };
            });

            var diag = DiagnosticMonitor.GetDefaultInitialConfiguration();
            diag.Logs.ScheduledTransferLogLevelFilter = LogLevel.Verbose;
            diag.Logs.ScheduledTransferPeriod = TimeSpan.FromMinutes(1.0);
            diag.WindowsEventLog.DataSources.Add("Application!*");
            diag.WindowsEventLog.DataSources.Add("System!*");
            diag.WindowsEventLog.ScheduledTransferPeriod = TimeSpan.FromMinutes(1.0);
            diag.DiagnosticInfrastructureLogs.ScheduledTransferPeriod = TimeSpan.FromMinutes(1.0);
            DiagnosticMonitor.Start("Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString", diag);

            _account = CloudStorageAccount.Parse(
                RoleEnvironment.GetConfigurationSettingValue("DataConnectionString"));
            if (!int.TryParse(RoleEnvironment.GetConfigurationSettingValue("MessageAmount"), out MsgAmt))
            {
                MsgAmt = 2;
            }
            return base.OnStart();
        }
    }
}
